package com.reebake.ideal.security.qrcode;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjUtil;
import com.reebake.ideal.security.authentication.AbstractAuthenticationProvider;
import com.reebake.ideal.security.code.CodeAuthenticationToken;
import com.reebake.ideal.security.entity.UserDetailsEntity;
import com.reebake.ideal.security.handler.UserDetailsServiceImpl;
import com.reebake.ideal.security.service.UserSecurityService;
import com.reebake.ideal.security.util.AuthenticationExceptionUtil;
import com.reebake.ideal.verify.code.core.VerifyCodeService;
import com.reebake.ideal.verify.code.entity.VerifyCodeEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;

@Slf4j
@RequiredArgsConstructor
public class QrcodeAuthenticationProvider extends AbstractAuthenticationProvider {
    private final UserSecurityService userSecurityService;
    private final VerifyCodeService verifyCodeService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        QrcodeAuthenticationToken authenticationToken = (QrcodeAuthenticationToken) authentication;
        VerifyCodeEntity verifyCodeEntity = (VerifyCodeEntity) authenticationToken.getCredentials();
        String username = verifyCodeEntity.getReceiver();
        String verifyCode = verifyCodeEntity.getVerifyCode();
        VerifyCodeEntity cacheVerifyCode = verifyCodeService.get(username);
        if(ObjUtil.isNull(cacheVerifyCode)) {
            throw new BadCredentialsException("code verify failure");
        }
        String userId = (String) cacheVerifyCode.getExtParam();
        Boolean verifyCodeResult = verifyCodeService.check(username, verifyCode);
        if (BooleanUtil.isFalse(verifyCodeResult)) {
            log.debug("qr code verify failure");
            throw AuthenticationExceptionUtil.throwBadCredential();
        }

        UserDetailsEntity userDetailsEntity = userSecurityService.loadUserByUserId(userId);

        if(userDetailsEntity == null) {
            log.debug("user:" + username + " not found.");
            throw AuthenticationExceptionUtil.throwUserNotFound(username);
        }
        UserDetails userDetails = UserDetailsServiceImpl.convert(username, userDetailsEntity);

        CodeAuthenticationToken authenticationResult = new CodeAuthenticationToken(userDetails, verifyCodeEntity, userDetails.getAuthorities());

        preAuthenticationChecks.check(userDetails);
        return authenticationResult;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return QrcodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

}
